﻿using System;
using System.Collections.Generic;
using System.Linq;
public class GA
{
    int population; // 种群大小
    double cross_ratio = 0.7; // 交叉率
    double muta_ratio = 0.1; // 变异率

    public GA(int pop)
    {
        population = pop;
    }

    private List<Genome> ListRandom(List<Genome> myList)
    {
        List<Genome> newList = new List<Genome>();
        int index = 0;
        Genome temp = null;
        for (int i = 0; i < myList.Count; i++)
        {

            index = UnityEngine.Random.Range(0, myList.Count - 1);
            if (index != i)
            {
                temp = myList[i];
                myList[i] = myList[index];
                myList[index] = temp;
            }
        }
        return myList;
    }

    private double[] GetOneBaby(List<Genome> parents)
    {
        List<List<Double>> children = new List<List<Double>>(population);
        // 取出父代中的精英个体(占总数的1/4)
        int elite_num = population / 4;
        parents.Sort((x, y) =>
        {
            if (x.fitness > y.fitness)
            {
                return 1;
            }
            else if (x.fitness < y.fitness)
            {
                return -1;
            }
            else
            {
                return 0;
            }
        });

        //string bestWeights = "";
        //foreach (var t in parents[parents.Count - 1].weights)
        //{
        //    bestWeights += t + ",";
        //}
        //UnityEngine.Debug.Log(bestWeights);

        Genome dad = GetParent(parents);
        Genome mum = GetParent(parents);
        double[] baby1 = null;
        double[] baby2 = null;
        CrossoverAtSplits(dad.splitPoints, dad.weights, mum.weights, out baby1, out baby2);
        if (UnityEngine.Random.Range(0, 100) > 50)
        {
            return baby1;
        }
        else
        {
            return baby2;
        }
    }


    //根据父代的基因，经过一轮的交叉和变异，得到子代的基因（神经网络的权重）
    public List<double[]> Run(List<Genome> parents)
    {
        List<double[]> children = new List<double[]>();
        // 取出父代中的精英个体(占总数的1/4)
        int elite_num = population / 4;
        parents.Sort((x, y) =>
        {
            if (x.fitness > y.fitness)
            {
                return 1;
            }
            else if (x.fitness < y.fitness)
            {
                return -1;
            }
            else
            {
                return 0;
            }
        });

        #region 输出
        double totalFit = 0;
        foreach (var t in parents)
        {
            totalFit += t.fitness;
        }
        UnityEngine.Debug.Log("最高适应性:" + parents[parents.Count - 1].fitness + "   平均适应性:" + totalFit / parents.Count);
        #endregion

        List<double[]> elite = new List<double[]>();
        for (int i = 0; i < elite_num; i++)
        {
            //UnityEngine.Debug.Log("i=" + i + "  fit=" + parents[population - i - 1].fitness);
            elite.Add(parents[population - i - 1].weights.CloneArr());
        }

        for (int i = 0; i < elite.Count; i++)
        {
            children.Add(elite[i].CloneArr());
        }
        //string s = "";
        //foreach (var t in parents[parents.Count - 1].weights)
        //{
        //    s += System.Math.Round(t, 2) + ",";
        //}
        //UnityEngine.Debug.Log(s);

        #region 杂交
        while (true)
        {
            Genome dad = GetParent(parents);
            Genome mum = GetParent(parents);
            double[] baby1 = null;
            double[] baby2 = null;
            CrossoverAtSplits(dad.splitPoints, dad.weights, mum.weights, out baby1, out baby2);
            children.Add(baby1);
            children.Add(baby2);
            int n = (int)(population * 3.6f / 4);
            if (children.Count >= n)
            {
                while (true)
                {
                    if (children.Count > n)
                    {
                        children.RemoveAt(children.Count - 1);
                    }
                    else
                    {
                        break;
                    }
                }
                break;
            }
        }
        #endregion

        #region 变异
        for (int i = population / 4 - 1; i < children.Count(); i++)
        {
            for (int j = 0; j < children[i].Length; j++)
            {
                if (UnityEngine.Random.Range(0f, 1f) < muta_ratio)
                {
                    double w = children[i][j];
                    w = UnityEngine.Random.Range(-1f, 1f);
                    children[i][j] = w;
                }
            }
        }
        #endregion


        while (population > children.Count)
        {
            double[] ws = new double[children[0].Length];
            for (int j = 0; j < ws.Length; j++)
            {
                ws[j] = UnityEngine.Random.Range(-1f, 1f);
            }
            children.Add(ws);
        }

        //string ss = "";
        //foreach (var t in children[0])
        //{
        //    ss += System.Math.Round(t, 2) + ",";
        //}
        //UnityEngine.Debug.Log(ss);

        //if(s!=ss)
        //{
        //    UnityEngine.Debug.LogError("精英个体出现问题");
        //}
        return children;
    }

    private Genome GetParent(List<Genome> parents)
    {
        double totalFit = 0;
        foreach (var t in parents)
        {
            totalFit += t.fitness;
        }
        float rand = UnityEngine.Random.Range(0f, (float)totalFit);
        double tempFit = 0;
        int index = UnityEngine.Random.Range(0, parents.Count);
        for (int i = 0; i < parents.Count; i++)
        {
            tempFit += parents[i].fitness;
            if (tempFit > rand)
            {
                index = i;
                break;
            }
        }
        return new Genome(parents[index].weights.CloneArr(), parents[index].fitness, parents[index].splitPoints);
    }

    private void CrossoverAtSplits(int[] splitPoints, double[] dad, double[] mum, out double[] baby1, out double[] baby2)
    {
        baby1 = new double[dad.Length];
        baby2 = new double[dad.Length];
        if ((UnityEngine.Random.Range(0f, 1f) > cross_ratio) || (mum == dad))
        {
            baby1 = mum.CloneArr();
            baby2 = dad.CloneArr();
            return;
        }
        int index1 = UnityEngine.Random.Range(0, splitPoints.Length - 2);
        int index2 = UnityEngine.Random.Range(index1, splitPoints.Length - 1);
        int cp1 = splitPoints[index1];
        int cp2 = splitPoints[index2];

        for (int i = 0; i < mum.Length; ++i)
        {
            if ((i < cp1) || (i >= cp2))
            {
                // 如果在杂交点外，保持原来的基因
                baby1[i] = mum[i];
                baby2[i] = dad[i];
            }
            else
            {
                // 把中间段进行交换
                baby1[i] = dad[i];
                baby2[i] = mum[i];
            }
        }
    }
}

public static class AAA
{
    public static double[] CloneArr(this double[] a)
    {
        double[] dl = new double[a.Length];
        for (int i = 0; i < a.Length; i++)
        {
            dl[i] = a[i];
        }
        return dl;
    }
}

